﻿using Common.Framework;
using Common.Framework.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace DBUtil.Builders
{
    internal class SetIgnoreItem
    {
        public SetIgnoreItem(EnumSetIgnoreType type, string[] cols, string[] colNoQuotes, object val, List<string> jsonDepthProps = null)
        {
            Ensure.NotNullOrEmpty(cols, nameof(cols));
            ColumnNames = new string[cols.Length];
            ColumnNamesNoQuote = new string[cols.Length];
            for (int i = 0; i < cols.Length; i++)
            {
                var colStr2 = cols[i];
                Ensure.NotNullOrEmptyOrWhiteSpace(colStr2, nameof(colStr2));
                var colNoQuote = colNoQuotes[i];
                this.ColumnNames[i] = colStr2;
                this.ColumnNamesNoQuote[i] = colNoQuote;
            }
            this.Type = type;
            this.Value = val;
            this.JsonDepthProps = jsonDepthProps;
        }
        public EnumSetIgnoreType Type { get; set; }
        public string[] ColumnNames { get; set; }
        public string[] ColumnNamesNoQuote { get; set; }
        public object Value { get; set; }
        public List<string> JsonDepthProps { get; set; }

        internal static void SetColumn(DBAccess db, List<SetIgnoreItem> setIgnores, string colname, object value)
        {
            setIgnores.Add(new SetIgnoreItem(EnumSetIgnoreType.SetColumn, [colname], [db.RemoveQuote(colname)], value));
        }

        internal static void IgnoreColumns(DBAccess db, List<SetIgnoreItem> setIgnores, params string[] colNames)
        {
            setIgnores.Add(new SetIgnoreItem(EnumSetIgnoreType.IgnoreColumn, colNames, colNames.Select(i => db.RemoveQuote(i)).ToArray(), null));
        }

        internal static void OnlyColumns(DBAccess db, List<SetIgnoreItem> setIgnores, params string[] colNames)
        {
            setIgnores.Add(new SetIgnoreItem(EnumSetIgnoreType.OnlyColumn, colNames, colNames.Select(i => db.RemoveQuote(i)).ToArray(), null));
        }

        internal static void SetColumn<T>(DBAccess db, List<SetIgnoreItem> setIgnores, EntityInfo entityInfo, Expression<Func<T, object>> propSelector, object value)
        {
            var names = GetInitOrReturnPropNames(propSelector);
            if (names.IsNullOrEmpty()) throw new Exception($"格式错误,无法获取指定的列: {propSelector}!");
            SetColumn(db, setIgnores, entityInfo, names, value);
        }

        internal static void SetColumn(DBAccess db, List<SetIgnoreItem> setIgnores, EntityInfo entityInfo, List<List<string>> names, object value)
        {
            for (var i = 0; i < names.Count; i++)
            {
                var propsNames = names[i];
                var prop = entityInfo.EntityPropertyInfos.FirstOrDefault(j => j.PropNamePure == propsNames[0]);
                if (prop == null) continue;
                if (propsNames.Count > 1)
                {
                    //JsonDepthProps
                    propsNames.RemoveAt(0);
                    var key = prop.JsonKey;
                    if (key.IsNotNullOrEmptyOrWhiteSpace()) propsNames.Insert(0, key);
                    setIgnores.Add(new SetIgnoreItem(EnumSetIgnoreType.SetColumn, [prop.ColumnNameSeg], [db.RemoveQuote(prop.ColumnNamePure)], value, propsNames));
                }
                else
                {
                    //当做普通列
                    setIgnores.Add(new SetIgnoreItem(EnumSetIgnoreType.SetColumn, [prop.ColumnNameSeg], [db.RemoveQuote(prop.ColumnNameSeg)], value));
                }
            }
        }

        internal static void IgnoreColumns<T>(DBAccess db, List<SetIgnoreItem> setIgnores, EntityInfo entityInfo, Expression<Func<T, object>> propSelector)
        {
            var names = ExpressionHelper.GetInitOrReturnPropNames(propSelector);
            var colNames = entityInfo.EntityPropertyInfos.Where(i => names.Contains(i.PropNamePure)).Select(i => i.ColumnNameSeg).ToArray();
            setIgnores.Add(new SetIgnoreItem(EnumSetIgnoreType.IgnoreColumn, colNames, colNames.Select(i => db.RemoveQuote(i)).ToArray(), null));
        }

        internal static void OnlyColumns<T>(DBAccess db, List<SetIgnoreItem> setIgnores, EntityInfo entityInfo, Expression<Func<T, object>> propSelector)
        {
            var names = ExpressionHelper.GetInitOrReturnPropNames(propSelector).ToArray();
            var colNames = entityInfo.EntityPropertyInfos.Where(i => names.Contains(i.PropNamePure)).Select(i => i.ColumnNameSeg).ToArray();
            setIgnores.Add(new SetIgnoreItem(EnumSetIgnoreType.OnlyColumn, colNames, colNames.Select(i => db.RemoveQuote(i)).ToArray(), null));
        }

        /// <summary>
        /// 获取深度属性名称列表, 传入:
        /// <code>
        /// i=>new {i.Ext.Detail.Name,i.Ext.Detaul.Id}
        /// </code>
        /// 返回:
        /// <code>
        /// [["Ext","Detail","Name"],["Ext","Detail","Id"]]
        /// </code>
        /// </summary>
        internal static List<List<string>> GetInitOrReturnPropNames(LambdaExpression expression)
        {
            if (expression.Body.NodeType == ExpressionType.Convert)
            {
                return Func((expression.Body as UnaryExpression).Operand);
            }
            return Func(expression.Body);

            List<List<string>> Func(Expression expression)
            {
                if (expression.NodeType == ExpressionType.MemberInit)
                {
                    //new CustomClass{...}
                    var names = new List<List<string>>();
                    var initExp = expression as MemberInitExpression;
                    foreach (var bind in initExp.Bindings)
                    {
                        if (bind is not MemberAssignment assign || assign?.Expression is not MemberExpression member) throw new Exception($"格式错误({bind}): 使用 new CustomClass{{...}} 构建的属性必须是 MemberAssignment/MemberExpression, 如: SetColumn(i=>new Person{{i.CreateTime,i.UpdateTime}})!");
                        var _names = new List<string>(4);
                        Visit(member, _names);
                        _names.Reverse();
                        names.Add(_names);
                    }
                    return names;
                }
                else if (expression.NodeType == ExpressionType.New)
                {
                    //new {...}
                    var names = new List<List<string>>();
                    var newExp = expression as NewExpression;
                    if (!newExp.Type.IsAnonymous()) throw new Exception($"格式错误({expression}): 使用 new() 构建的必须是匿名类,如: SetColumn(i=>new{{i.CreateTime,i.UpdateTime}})!");
                    foreach (var arg in newExp.Arguments)
                    {
                        if (arg is not MemberExpression member) throw new Exception($"格式错误({arg}): 使用 new() 构建的匿名类属性必须是 MemberExpression, 如: SetColumn(i=>new{{i.CreateTime,i.UpdateTime}})!");
                        var _names = new List<string>(4);
                        Visit(expression, _names);
                        _names.Reverse();
                        names.Add(_names);
                    }
                    return names;
                }
                else if (expression.NodeType == ExpressionType.Parameter)
                {
                    // p=>p
                    throw new Exception($"格式错误, 不允许使用【i=>i】作为 SetColumn() 的参数, 正确的示例: SetColumn(i=>i.Name) 或 SetColumn(i=>new{{i.CreateTime,i.UpdateTime}})!");
                }
                else if (expression.NodeType == ExpressionType.MemberAccess)
                {
                    //p=>p.Id
                    var _names = new List<string>(4);
                    Visit(expression, _names);
                    _names.Reverse();
                    return [_names];
                }
                else if (expression is MethodCallExpression call)
                {
                    if (call.Type.FullName == "System.Text.Json.Nodes.JsonNode" && call.Method.GetMethodFullName() == "System.Text.Json.Nodes.JsonNode System.Text.Json.Nodes.JsonNode.get_Item(string propertyName)")
                    {
                        //p=>p.Ext["Detail"]
                        var _names = new List<string>(4);
                        Visit(call, _names);
                        _names.Reverse();
                        return [_names];
                    }
                    else if (call.Object?.Type.Implements<System.Collections.IDictionary>() == true)
                    {
                        //p=>p.Ext["Detail"]
                        var _names = new List<string>(4);
                        Visit(call, _names);
                        _names.Reverse();
                        return [_names];
                    }
                }

                throw new Exception($"格式错误: {expression}");
            }

            void Visit(Expression expression, List<string> names)
            {
                if (expression is MemberExpression memberExpression)
                {
                    //p=>p.Ext.Detail.Name
                    //过滤掉 SetColumn(i=>i.Age.Value,10) 中的 Value
                    if (memberExpression.Member.Name != "Value" || !memberExpression.Expression.Type.IsNullable())
                    {
                        names.Add(memberExpression.Member.Name);
                    }
                    if (memberExpression.Expression is MemberExpression memberExpression2) Visit(memberExpression2, names);
                }
                else if (expression is MethodCallExpression callExpression)
                {
                    //p=>p.Ext["Detail"]["Name"]
                    names.Add((callExpression.Arguments[0] as ConstantExpression).Value.ToString());
                    if (callExpression.Object is MethodCallExpression callExpression2 && callExpression2.Type.FullName == "System.Text.Json.Nodes.JsonNode" && callExpression2.Method.GetMethodFullName() == "System.Text.Json.Nodes.JsonNode System.Text.Json.Nodes.JsonNode.get_Item(string propertyName)")
                    {
                        Visit(callExpression2, names);
                    }
                    else if (callExpression.Object is MethodCallExpression callExpression3 && callExpression3.Object?.Type.Implements<System.Collections.IDictionary>() == true)
                    {
                        Visit(callExpression3, names);
                    }
                    else if (callExpression.Object is MemberExpression memberExpression2)
                    {
                        Visit(memberExpression2, names);
                    }
                }
            }
        }
    }

    internal class SetListItem
    {
        public SetListItem(EntityPropertyInfo propertyInfo, string columnName, string columnNameNoQuote, object value, bool fromSet, List<string> jsonDepthProps = null)
        {
            PropertyInfo = propertyInfo;
            ColumnName = columnName;
            ColumnNameNoQuote = columnNameNoQuote;
            Value = value;
            FromSet = fromSet;
            JsonDepthProps = jsonDepthProps;
        }

        public EntityPropertyInfo PropertyInfo { get; set; }
        public string ColumnName { get; set; }
        public string ColumnNameNoQuote { get; set; }
        public object Value { get; set; }
        public bool FromSet { get; set; }
        public List<string> JsonDepthProps { get; set; }
    }

    public class BaseBuilder
    {
        internal DBAccess db { get; private set; }

        internal BaseBuilder(DBAccess db)
        {
            Ensure.NotNull(db, nameof(db));
            this.db = db;
        }

        #region 超时设置
        private int? _timeoutSeconds { get; set; }
        protected int? TimeoutSeconds => _timeoutSeconds;
        public virtual BaseBuilder CommandTimeout(int timeoutSeconds)
        {
            _timeoutSeconds = timeoutSeconds;
            return this;
        }
        public virtual BaseBuilder CommandTimeoutIf(bool condition, int timeoutSeconds)
            => condition ? CommandTimeout(timeoutSeconds) : this;
        #endregion
        #region RunWriteMonitor
        protected T RunWriteMonitor<T>(AfterWriteArgument arg, Func<T> func)
        {
            var res = func();
            if (db.Setting.HasAfterWriteMonitor) db.Setting.AfterWriteMonitorAction(arg).Wait();
            return res;
        }
        protected async Task<T> RunWriteMonitorAsync<T>(AfterWriteArgument arg, Func<Task<T>> func)
        {
            var res = await func();
            if (db.Setting.HasAfterWriteMonitor) await db.Setting.AfterWriteMonitorAction(arg);
            return res;
        }
        #endregion
    }

    public class WhereBuilder : BaseBuilder
    {
        private struct WhereSegWrap(object value)
        {
            public object Value = value;
        }
        internal WhereBuilder(DBAccess db) : base(db) { }
        protected List<object> Filters { get; set; } = [];

        internal WhereBuilder SetFilters(List<object> filters)
        {
            this.Filters = filters;
            return this;
        }

        #region WhereSeg
        public virtual WhereBuilder WhereSeg<TAny>(Expression<Func<TAny, bool>> filter)
        {
            Ensure.NotNull(filter, nameof(filter));
            Filters.Add(new WhereSegWrap(filter));
            return this;
        }
        public virtual WhereBuilder WhereSegIf<TAny>(bool condition, Expression<Func<TAny, bool>> filter)
        {
            if (!condition) return this;
            return WhereSeg(filter);
        }
        public virtual WhereBuilder WhereSeg<TAny, TAny2>(Expression<Func<TAny, TAny2, bool>> filter)
        {
            Ensure.NotNull(filter, nameof(filter));
            Filters.Add(new WhereSegWrap(filter));
            return this;
        }
        public virtual WhereBuilder WhereSegIf<TAny, TAny2>(bool condition, Expression<Func<TAny, TAny2, bool>> filter)
        {
            if (!condition) return this;
            return WhereSeg(filter);
        }
        public virtual WhereBuilder WhereSeg<TAny, TAny2, TAny3>(Expression<Func<TAny, TAny2, TAny3, bool>> filter)
        {
            Ensure.NotNull(filter, nameof(filter));
            Filters.Add(new WhereSegWrap(filter));
            return this;
        }
        public virtual WhereBuilder WhereSegIf<TAny, TAny2, TAny3>(bool condition, Expression<Func<TAny, TAny2, TAny3, bool>> filter)
        {
            if (!condition) return this;
            return WhereSeg(filter);
        }
        public virtual WhereBuilder WhereSeg<TAny, TAny2, TAny3, TAny4>(Expression<Func<TAny, TAny2, TAny3, TAny4, bool>> filter)
        {
            Ensure.NotNull(filter, nameof(filter));
            Filters.Add(new WhereSegWrap(filter));
            return this;
        }
        public virtual WhereBuilder WhereSegIf<TAny, TAny2, TAny3, TAny4>(bool condition, Expression<Func<TAny, TAny2, TAny3, TAny4, bool>> filter)
        {
            if (!condition) return this;
            return WhereSeg(filter);
        }
        #endregion
        #region Where
        public virtual WhereBuilder Where(string filter)
        {
            Ensure.NotNullOrEmptyOrWhiteSpace(filter);
            Filters.Add(filter);
            return this;
        }
        public virtual WhereBuilder WhereIf(bool condition, string filter)
        {
            if (!condition) return this;
            return Where(filter);
        }
        #endregion  

        /// <summary>
        /// 处理 Filter,兼容
        /// <list type="bullet">
        /// <item>id=1</item>
        /// <item>(id,name)=>id>10</item>
        /// <item>i=>i.id==1</item>
        /// </list>
        /// </summary>
        protected string DealFilter(List<object> Filters, Dictionary<ParameterExpression, object> midValues = null, IEnumerable<KeyValuePair<ParameterExpression, string>> aliases = null)
        {
            Ensure.NotNullOrEmpty(Filters);
            //处理过滤条件
            var filterSqls = new List<string>();
            if (Filters.IsNotNullOrEmpty())
            {
                for (int i = 0; i < Filters.Count; i++)
                {
                    var filter = Filters[i];
                    var isSeg = false;
                    if (filter is WhereSegWrap wrap)
                    {
                        isSeg = true;
                        filter = wrap.Value;
                    }
                    if (filter is string str)
                    {
                        filterSqls.Add(str);
                        continue;
                    }
                    if (filter is not LambdaExpression lambda) throw new Exception($"必须为LambdaExpression!");
                    var reduce = ExpressionHelper.ReduceLambda(lambda, initParameters: aliases?.Select(i => i.Key).ToList(), isKeepCallBack: BuilderHelper.IsKeepCallBackForWhere);
                    if (midValues.IsNotNullOrEmpty()) reduce.midValues.Add(midValues);
                    midValues = reduce.midValues;

                    var ret = BuilderHelper.GetConstant(reduce.exp, midValues);
                    if (ret.Success && ret.Data is bool flag)
                    {
                        filterSqls.Add(flag ? "1=1" : "1=0");
                        continue;
                    }
                    lambda = reduce.exp as LambdaExpression;
                    string tmp = string.Empty;
                    List<KeyValuePair<ParameterExpression, string>> aliasesMap = null;
                    if (isSeg) aliasesMap = GetWhereSegAliasRule(lambda);
                    else if (this is not SelectBuilderBase)
                    {
                        //非查询,如: update/delete 需要禁用别名
                        aliasesMap = lambda.Parameters.Select(i => new KeyValuePair<ParameterExpression, string>(i, null)).ToList();
                    }
                    aliasesMap ??= [];
                    if (aliases.IsNotNullOrEmpty()) aliasesMap.AddRange(aliases);
                    tmp = BuilderHelper.ParseSql(lambda, db, aliasesMap: aliasesMap, midValues: midValues);
                    if (tmp.IsNotNullOrEmptyOrWhiteSpace()) filterSqls.Add(tmp);
                }
            }
            if (filterSqls.Count == 0) return string.Empty;
            else if (filterSqls.Count == 1) return filterSqls.FirstOrDefault();
            else return filterSqls.Select(i => $"({i})").ToStringSeparated(" and ");
        }

        /// <summary>
        /// 计算 WhereSeg 中参数的别名
        /// </summary>
        protected virtual List<KeyValuePair<ParameterExpression, string>> GetWhereSegAliasRule(LambdaExpression lambda)
        {
            if (lambda.Parameters.Count == 0) return null;
            #region 思路
            // 在 builder 下没有输入 alias 的机会
            // 简单类型: WhereSeg<int>(age=>age>10) 注意: 不能是 _=>_>10 抛异常
            //     update/insert:
            //         =>sql: age>10
            //     select: 
            //         只有一个Entity的时候
            //             =>sql: t.age>10
            //         否则使用最后一个的:
            //             =>sql: t3.age>10
            //
            // class:
            //     WhereSeg<Person>(i=>i.age>10)            
            //     update/delete:
            //         => age>10
            //     select:
            //         如果是 "_" 则直接取最后一个entity的别名
            //         有且只有一个匹配到的 Entity 的时, 使用匹配到的entity:
            //             sql: t2.age>10
            //         否则:
            //             sql: i.age>10
            #endregion
            var map = new List<KeyValuePair<ParameterExpression, string>>(lambda.Parameters.Count);
            var isSelect = this is SelectBuilderBase;

            for (var i = 0; i < lambda.Parameters.Count; i++)
            {
                var param = lambda.Parameters[i];
                if (DBAccess.IsSimple(param.Type))
                {
                    // WhereSeg<int>(age=>age>10)
                    if (param.Name == "_") throw new Exception($"'_' 错误, 禁止写法: [select|insert|update]Builder.WhereSeg<int>(_ => _ > 0)");
                    if (isSelect)
                    {
                        var selectBase = this as SelectBuilderBase;
                        var alias = selectBase.EntityAliases.Last().Alias;
                        map.Add(new KeyValuePair<ParameterExpression, string>(param, alias));
                    }
                    else
                    {
                        map.Add(new KeyValuePair<ParameterExpression, string>(param, param.Name));
                    }
                }
                else
                {
                    //WhereSeg<Person>(i=>i.age>10)
                    if (isSelect)
                    {
                        var selectBase = this as SelectBuilderBase;
                        var alias = selectBase.EntityAliases.Last().Alias;
                        if (param.Name == "_")
                        {
                            map.Add(new KeyValuePair<ParameterExpression, string>(param, alias));
                        }
                        else
                        {
                            var arr = selectBase.EntityAliases.Select(i => i.EntityInfo.Type).ToList();
                            var idx = arr.FindIndex(i => i == param.Type);
                            var idx2 = arr.FindLastIndex(i => i == param.Type);
                            if (idx >= 0 && idx == idx2)
                            {
                                //只有一个类型,如: SelectBuilder<Person,Student,Dog>().WhereSeg<Person>(i=>)
                                map.Add(new KeyValuePair<ParameterExpression, string>(param, selectBase.EntityAliases[idx].Alias));
                            }
                            else
                            {
                                //有多个相同的类型,如: SelectBuilder<Person,Student,Person>().WhereSeg<Person>(i=>)
                                //这种时候就不再从 SelectBuilder<>.Alias() 中找, 直接使用 WhereSeg<Person>(i=>) 中的
                                map.Add(new KeyValuePair<ParameterExpression, string>(param, param.Name));
                            }
                        }
                    }
                    else
                    {
                        map.Add(new KeyValuePair<ParameterExpression, string>(param, null));
                    }
                }
            }
            return map;
        }
    }
}
